package com.yhn.init.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yhn.init.common.ErrorCode;
import com.yhn.init.constant.UserConstant;
import com.yhn.init.exception.ThrowUtils;
import com.yhn.init.mapper.AttendanceMapper;
import com.yhn.init.model.entity.Attendance;
import com.yhn.init.model.vo.LoginUserVO;
import com.yhn.init.service.AttendanceService;
import com.yhn.init.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;

/**
 * @author yhnyhn123
 */
@Slf4j
@Service("attendanceService")
public class AttendanceServiceImpl extends ServiceImpl<AttendanceMapper, Attendance> implements AttendanceService {

    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMM");
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private UserService userService;

    @Override
    public Integer sign() {
        // 获取登录用户信息
        LoginUserVO loginUser = userService.getLoginUser();
        Long userId = loginUser.getId();
        // 获取日期
        LocalDateTime today = LocalDateTime.now();
        // 获取当前月份的天数（从1到最大天数）
        int offset = today.getDayOfMonth() - 1;
        // 构建 Key furniture:attendance:userID:yyyyMM
        String signKey = buildSignKey(userId, today);
        // 查看是否已签到
        boolean isSigned = Boolean.TRUE.equals(stringRedisTemplate.opsForValue().getBit(signKey, offset));
        ThrowUtils.throwIf(isSigned, ErrorCode.OPERATION_ERROR, "今日已经签到过了");
        // 签到
        stringRedisTemplate.opsForValue().setBit(signKey, offset, true);
        // 统计连续签到的次数
        return getContinuousSignCount(userId, today);
    }

    /**
     * 统计连续签到的次数
     *
     * @param userId 用户ID
     * @param date   当前日期
     * @return 连续签到的天数
     */
    private int getContinuousSignCount(Long userId, LocalDateTime date) {
        // 获取日期对应的天数，多少号，假设是 30
        int dayOfMonth = date.getDayOfMonth();

        // 构建 Key
        String signKey = buildSignKey(userId, date);


        // bitfield user:sign:5:202212 u30 0
        //获取本月到今天为止所有的签到记录，返回了一个十进制数字 bitfield key GET u[dayOFMonth] 0
        //从0开始查，一共查【dayOFMonth】个bit位
        BitFieldSubCommands bitFieldSubCommands = BitFieldSubCommands.create()
                .get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth))
                .valueAt(0);
        List<Long> list = stringRedisTemplate.opsForValue().bitField(signKey, bitFieldSubCommands);
        if (list == null || list.isEmpty()) {
            return 0;
        }
        int signCount = 0;
        //取出num值[10进制数]
        Long num = list.get(0);
        if (num == null || num == 0) {
            return 0;
        }
        //循环遍历
        //让这个数字与1做与运算，得到数字最后一个bit位
        //判断bit是否为0
        // i 表示位移操作.次数
        while ((num & 1) != 0) {
            //不为0说明已签到，次数+1
            signCount++;
            //把数字位右移一位抛弃最后一个bit位
            num = num >>> 1;
        }
        return signCount;
    }

    /**
     * 构建 Redis的Key -- furniture:user:id:yyyyMM
     *
     * @param userId 用户ID
     * @param date   日期
     * @return rediskey
     */
    private String buildSignKey(Long userId, LocalDateTime date) {
        return String.format(UserConstant.USER_ATTENDANCE_KEY + "%d:%s", userId,
                date.format(DATE_FORMATTER));
    }


}
